package main

import (
	"errors"
	"fmt"
	"io"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"gopkg.in/mgo.v2/bson"
	"server/src/protocol808"
	//"smartu365.com/protocol808"
)

const (
	BUFFERSIZE        int    = 1024
	HEARTBEATINTERVAL int    = 300
	TESTRID           string = "088888888888"
)

type ReceiverConnection struct {
	conn         *net.Conn
	terminalId   string
	ip           string
	dbo          *DBOperation
	manager      *ConnManager
	blacklist    *IpBlacklist
	readBuffer   []byte
	serialNo     uint16
	authCode     string
	authorized   bool
	getProperty  bool
	getParameter bool

	lastCommunicateTime int64
	mux                 sync.Mutex
}

func NewReceiverConn(conn *net.Conn, dbo *DBOperation, manager *ConnManager, blacklist *IpBlacklist) *ReceiverConnection {
	mc := ReceiverConnection{}
	mc.conn = conn
	mc.dbo = dbo
	mc.manager = manager
	mc.blacklist = blacklist
	mc.readBuffer = make([]byte, BUFFERSIZE)
	mc.serialNo = 0
	mc.authorized = false
	mc.getProperty = false

	return &mc
}

func (p *ReceiverConnection) Disconnect() {
	(*p.conn).Close()
}

func (p *ReceiverConnection) Do() {
	protocol := protocol808.Request{}
	var header *protocol808.Header
	var body string
	var err error
	var n int
	var objectId bson.ObjectId
	var cmdOid bson.ObjectId
	var responseStr string
	var sendStr string

	var partContent string
	var contents []string
	var content string
	var buf []byte

	for {
		responseStr = ""

		n, err = (*p.conn).Read(p.readBuffer)
		if err != nil {
			if err != io.EOF {
				zLog(p.conn, " 发生异常:", err.Error())
			}
			return
		} else if n == 0 {
			zLog(p.conn, " 没有读到数据")
			continue
		}

		(*p.conn).SetDeadline(time.Now().Add(time.Second * time.Duration(HEARTBEATINTERVAL)))

		p.lastCommunicateTime = time.Now().Unix()
		if p.terminalId != "" {
			p.dbo.SetLastCommunicateTime(p.terminalId)
		}

		buf = p.readBuffer[:n]

		content = p.byte2HexStr(buf)
		zLog(p.conn, p.terminalId, "收到数据：", content)

		content = partContent + content

		contents, partContent = p.split(content)
		for _, v := range contents {
			zLog(p.conn, p.terminalId, "解析数据：", v)
			err, header, body = protocol.Parse(v)
			if err == nil {

				objectId = p.dbo.SaveRawData(header.TerminalId, header.MessageType, v)
				//记录日志
				zLog(p.conn, p.terminalId, "收到数据,包类别:", header.MessageType, ",内容：", string(content))

				if p.terminalId == "" && header.MessageType != protocol808.PTRegister &&
					header.MessageType != protocol808.PTAuthentication {
					zLog(p.conn, " 设备还没有注册，不能发送其它消息")

					//continue
				}

				switch header.MessageType {
				case protocol808.PTRegister:
					responseStr, err = p.TerminalRegister(header, body)
					if err != nil {

						go func() {
							ip := (*p.conn).RemoteAddr().String()
							count := p.blacklist.Add(ip)
							p.dbo.RecordIllegal(p.blacklist.TrimIp(ip), count, header.TerminalId)
						}()
					} else {
						zLog(p.conn, "设备", header.TerminalId, "注册成功")
					}
				case protocol808.PTAuthentication:
					responseStr, err = p.TerminalAuthorition(header, body)
					if err == nil {

						p.manager.Add(p)

						p.dbo.SetReceiverStatus(p.terminalId, ReceiverStatusOnline)
						zLog(p.conn, "设备", header.TerminalId, "鉴权成功")
					}
				case protocol808.PTAnswerProperty:
					_, _ = p.TerminalAnswerProperty(header, body)
					p.getProperty = true
				case protocol808.PTAnswerParameter:

					responseStr, err = p.TerminalAnswerParameter(header, body)
					p.getParameter = true
				case protocol808.PTCommandDown:
					zLog(p.conn, "设备", header.TerminalId, "回复下发指令:", body)
				case protocol808.PTTerminalAnswer:
					_, err = p.TerminalCommonAnswer(header, body)
				case protocol808.PTHearbeat:
				default:
					responseStr, err = p.CommonResponse(header)
				}

				go p.dbo.SaveRawDataReply(objectId, responseStr, err)

				var err1 error

				if responseStr != "" {
					buf = p.hexStr2Bytes(responseStr)

					_, err1 = (*p.conn).Write(buf)

					zLog(p.conn, p.terminalId, "回复消息：", responseStr)
					if err1 != nil {
						zLog(p.conn, p.terminalId, "回复消息出错：", err1.Error())
					}

					time.Sleep(time.Microsecond * 200)
				}

				if p.terminalId != "" {
					if !p.getProperty {
						p.getProperty = true
						sendStr = p.BlankResponse(protocol808.PTGetProperty, p.terminalId)
						buf = p.hexStr2Bytes(sendStr)
						err1 = p.SafeSend(buf)
						if err1 != nil {
							zLog(p.conn, p.terminalId, "发送查询终端属性指令：", sendStr, " 出错：", err1.Error())
						} else {
							zLog(p.conn, p.terminalId, "发送查询终端属性指令：", sendStr, " 成功")
						}
					} else if !p.getParameter {
						p.getParameter = true
						sendStr = p.BlankResponse(protocol808.PTGetParameter, p.terminalId)
						buf = p.hexStr2Bytes(sendStr)
						err1 = p.SafeSend(buf)
						if err1 != nil {
							zLog(p.conn, p.terminalId, "发送查询终端参数指令：", sendStr, " 出错：", err1.Error())
						} else {
							zLog(p.conn, p.terminalId, "发送查询终端参数指令：", sendStr, " 成功")
						}
					} else {
						cmdOid, sendStr = p.dbo.GetANewCommand(p.terminalId)
						if sendStr != "" {
							buf = p.hexStr2Bytes(sendStr)
							_, err1 = (*p.conn).Write(buf)
							if err1 != nil {
								zLog(p.conn, p.terminalId, "发送指令：", sendStr, " 出错：", err1.Error())
							} else {
								p.dbo.SetCommandStatus(cmdOid, CommandStatusSent)
								zLog(p.conn, p.terminalId, "发送指令：", sendStr, " 成功")
							}
						}
					}
				}
			} else {
				zLog(p.conn, p.terminalId, "收到数据(bytes)：", content)
				zLog(p.conn, p.terminalId, "收到数据(string)：", string(content))
				zLog(p.conn, p.terminalId, "解析失败：", err.Error())
				break
			}

			if err != nil {
				zLog(p.conn, "错误：", err.Error())
				break
			}
		}
	}
}

func (p *ReceiverConnection) TerminalRegister(header *protocol808.Header, body string) (response string, err error) {
	register := protocol808.Register{}
	register.Set(body)

	err = p.dbo.Register(header.TerminalId, register.IMEI)
	p.serialNo++

	res := protocol808.ResponseRegister{}
	res.MessageType = protocol808.PTRegisterAnswer
	res.SerialNo = p.serialNo
	res.RequestSeriaNo = header.MessageSerialNumber
	res.TerminalId = header.TerminalId
	authCode := strconv.Itoa(int(time.Now().Unix()))
	authCode = strings.Replace(authCode, "0", "A", -1)
	res.AuthCode = authCode
	if err == nil {
		p.terminalId = ""

		p.authCode = ""
		res.Result = protocol808.ResultSuccess

		p.dbo.SaveAuthCode(res.TerminalId, res.AuthCode)
	} else {
		res.Result = protocol808.ResultFail
	}
	response = res.Pack()

	return
}

func (p *ReceiverConnection) TerminalAuthorition(header *protocol808.Header, body string) (response string, err error) {
	p.serialNo++

	auth := protocol808.Authentication{}
	auth.Set(body)

	res := protocol808.Response{}
	res.MessageType = protocol808.PTServerAnswer
	res.SerialNo = p.serialNo
	res.RequestSeriaNo = header.MessageSerialNumber
	res.RequestMessageType = header.MessageType
	res.TerminalId = header.TerminalId

	p.terminalId = header.TerminalId
	p.authCode = p.dbo.GetAuthCode(header.TerminalId)

	if (header.TerminalId == TESTRID) || (header.TerminalId == p.terminalId && auth.AuthCode == p.authCode) {
		res.Result = protocol808.ResultSuccess + time.Now().Format("060102150405")
		p.authorized = true
	} else {
		err = errors.New(p.terminalId + " 鉴权失败,数据库中鉴权码为" + p.authCode + ",设备发送的为" + auth.AuthCode)
		res.Result = protocol808.ResultFail
	}
	response = res.Pack()

	return
}

func (p *ReceiverConnection) TerminalAnswerProperty(header *protocol808.Header, body string) (response string, err error) {
	p.serialNo++

	answer := protocol808.AnswerProperty{}
	answer.Set(body)
	property := ReceiverPropertyT{
		ICCID:               answer.ICCID,
		HardwareVersion:     answer.HardwareVersion,
		FirmwareProjectCode: answer.FirmwareProjectCode,
		FirmwareVersion:     answer.FirmwareVersion,
		TMPSModuleVersion:   answer.TPMSModuleVersion,
	}
	go p.dbo.SaveReceiverProperty(header.TerminalId, &property)

	return p.CommonResponse(header)
}

func (p *ReceiverConnection) TerminalAnswerParameter(header *protocol808.Header, body string) (response string, err error) {
	p.serialNo++

	answer := protocol808.AnswerParamenter{}
	answer.Set(body)
	parameter := ReceiverParamenterT{ServerIP: answer.ServerIP, ServerPort: answer.ServerPort,
		GPSInterval: answer.GPSInterval, TirePressureInterval: answer.TirePressureInterval,
		DeviceSleep: answer.DeviceSleep}

	go p.dbo.SaveReceiverParameter(header.TerminalId, &parameter)
	newdIP, newPort, err := p.dbo.GetReceiverNewServer(header.TerminalId)
	if err == nil {
		param := protocol808.CommandSetServer{}
		param.SerialNo = p.serialNo
		param.ServerIP = newdIP
		param.ServerPort = newPort
		param.TerminalId = header.TerminalId
		response, _ = param.Pack()
	}

	return
}

func (p *ReceiverConnection) TerminalCommonAnswer(header *protocol808.Header, body string) (response string, err error) {
	answer := protocol808.TerminalCommonAnswer{}
	answer.Set(body)

	go p.dbo.TerminalCommonAnswer(header.TerminalId, answer.ServerMessageType, answer.ServerSerialNo, answer.Result)

	return "", nil
}

func (p *ReceiverConnection) CommonResponse(header *protocol808.Header) (response string, err error) {
	p.serialNo++

	res := protocol808.Response{}
	res.MessageType = protocol808.PTServerAnswer
	res.SerialNo = p.serialNo
	res.RequestSeriaNo = header.MessageSerialNumber
	res.RequestMessageType = header.MessageType
	res.TerminalId = header.TerminalId
	res.Result = protocol808.ResultSuccess
	response = res.Pack()

	return
}

func (p *ReceiverConnection) BlankResponse(msgType string, terminalId string) string {
	p.serialNo++

	res := protocol808.Response{}
	res.MessageType = msgType
	res.SerialNo = p.serialNo
	res.TerminalId = terminalId
	response := res.Pack()

	return response
}

func (p *ReceiverConnection) split(str string) ([]string, string) {
	var strs = make([]string, 0)

	var index int
	var substr string

	for {
		index = strings.Index(str, "7E7E")
		if index%2 == 0 {
			substr = str[:index+2]
			str = str[index+2:]
		} else {
			substr = str
			str = ""
		}
		strs = append(strs, substr)
		if str == "" {
			break
		}
	}
	substr = strs[len(strs)-1]
	if substr[len(substr)-2:] != "7E" {
		strs = strs[:len(strs)-1]
		return strs, substr
	}

	return strs, ""
}

func (p *ReceiverConnection) byte2HexStr(content []byte) string {
	var res string

	for _, v := range content {
		res += fmt.Sprintf("%02X", v)
	}

	return res
}

func (p *ReceiverConnection) hexStr2Bytes(content string) []byte {
	var res = make([]byte, 0, len(content)/2)
	var sTemp string
	var iTemp int64

	for i := 0; i < len(content); i += 2 {
		sTemp = content[i : i+2]
		iTemp, _ = strconv.ParseInt(sTemp, 16, 0)
		res = append(res, byte(iTemp))
	}

	return res
}

func (p *ReceiverConnection) SafeSend(buf []byte) error {
	p.mux.Lock()
	defer p.mux.Unlock()

	_, err := (*p.conn).Write(buf)
	time.Sleep(time.Microsecond * 200)
	return err
}
